﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows;
using System.Windows.Data;

namespace CjcPhotoPrint
{
	public class TransformationContext : INotifyPropertyChanged
	{
		private Transformation transformation;
		private TransformationContext parent;
		private Drawing drawing;
		private bool enabled = true;

		public TransformationContext( Transformable source, TransformationContext parent, Transformation transformation )
		{
			this.Source = source;
			this.Parent = parent;
			this.Transformation = transformation;

			if ( parent == null && source != null ) source.PropertyChanged += OnPropertyChanged;
			if ( transformation != null ) transformation.PropertyChanged += OnPropertyChanged;
		}

		public bool IsIdentity { get { return transformation is Identity; } }
		public Transformable Source { get; set; }

		public TransformationContext Parent
		{
			get { return parent; }
			set
			{
				if ( parent != null ) parent.PropertyChanged -= OnPropertyChanged;
				parent = value;
				if ( parent != null ) parent.PropertyChanged += OnPropertyChanged;
				OnPropertyChanged( "Drawing" );
			}
		}

		public bool Enabled
		{
			get { return enabled; }
			set { enabled = value; OnPropertyChanged( "Enabled" ); OnPropertyChanged( "Drawing" ); }
		}

		public virtual double AspectRatio
		{
			get
			{
				var drawing = Drawing;

				return ( drawing != null )
					? drawing.Bounds.Width / drawing.Bounds.Height
					: ( parent != null ) ? parent.AspectRatio : 1;
			}
		}

		public Transformation Transformation
		{
			get { return transformation; }
			set
			{
				if ( transformation != null ) transformation.PropertyChanged -= OnPropertyChanged;
				if ( value != null ) value.PropertyChanged += OnPropertyChanged;
				transformation = value;
			}
		}

		public virtual string Name
		{
			get { return ( Transformation != null ) ? Transformation.Name : "(None)"; }
		}

		public Size ConstrainSize( Size size, Crop crop )
		{
			var constraint = crop.Constraint;
			if ( constraint == null || !constraint.AspectRatio.HasValue ) return size;

			size = Crop.Constrain( size );

			var parentSize = Crop.RelativeSize( parent.AspectRatio );

			var cropSize = new Size(
				parentSize.Width * size.Width,
				parentSize.Height * size.Height );

			var boundsAspect = cropSize.Width / cropSize.Height;

			return ( boundsAspect > crop.AspectRatio.Value )
				? new Size( size.Width * ( crop.AspectRatio.Value / boundsAspect ), size.Height )
				: new Size( size.Width, size.Height *= ( boundsAspect / crop.AspectRatio.Value ) );
		}
/*
		public Rect ConstrainBounds( Rect bounds, Crop crop )
		{
			var size = ConstrainSize( bounds, crop );

			if ( bounds.TopLeft != crop.Bounds.TopLeft )
			{
				bounds = new Rect(
					new Point( crop.Bounds.Right - size.Width, crop.Bounds.Bottom - size.Height ),
					crop.Bounds.BottomRight );
			}
			else if ( bounds.TopRight != crop.Bounds.TopRight )
			{
				bounds = new Rect(
					new Point( crop.Bounds.Left, crop.Bounds.Bottom - size.Height ),
					new Point( crop.Bounds.Left + size.Width, crop.Bounds.Bottom ) );
			}
			else if ( bounds.BottomLeft != crop.Bounds.BottomLeft )
			{
				bounds = new Rect(
					new Point( crop.Bounds.Right - size.Width, crop.Bounds.Top ),
					new Point( crop.Bounds.Right, crop.Bounds.Top + size.Height ) );
			}
			else if ( bounds.BottomRight != crop.Bounds.BottomRight )
			{
				bounds = new Rect(
					crop.Bounds.TopLeft,
					new Point( crop.Bounds.Left + size.Width, crop.Bounds.Top + size.Height ) );
			}

			return bounds;
		}
*/
		public Drawing Drawing
		{
			get
			{
				var parent = Parent;
				var parentDrawing = ( parent != null ) ? parent.Drawing : Source.BaseDrawing;

				if ( drawing == null )
				{
					var transformation = Transformation;
					if ( transformation != null ) drawing = transformation.GetDrawing( parentDrawing );
				}

				return enabled ? drawing : parentDrawing;
			}
		}

		public virtual TransformationContext AddRotate()
		{
			return AddTransformation( new Rotate() );
		}

		public virtual TransformationContext AddCrop()
		{
			return AddTransformation( new Crop { IsLandscape = AspectRatio > 1 } );
		}

		public virtual TransformationContext AddStraighten()
		{
			return AddTransformation( new Straighten() );
		}

		public virtual TransformationContext AddTransformation( Transformation transformation )
		{
			return Source.InsertTransformation( this.transformation.ParentOverride( this ), transformation );
		}

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		protected void OnPropertyChanged( object sender, PropertyChangedEventArgs e )
		{
			if ( e.PropertyName == "BaseDrawing" ) OnPropertyChanged( "Drawing" );

			if ( !( sender is Transformable ) ) OnPropertyChanged( e.PropertyName );
			//			if ( PropertyChanged != null ) PropertyChanged( this, e );
		}

		protected void OnPropertyChanged( string propertyName )
		{
			drawing = null;

			if ( ( propertyName == "IsLandscape" || propertyName == "AspectRatio" ) && transformation is Crop )
			{
				var crop = transformation as Crop;
				var size = ConstrainSize( new Size( 1, 1 ), crop );

				crop.Bounds = new Rect(
					new Point( ( 1 - size.Width ) / 2, ( 1 - size.Height ) / 2 ),
					size );
			}

			if ( PropertyChanged != null ) PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
		}

		#endregion

		#region IValueConverter Members

		public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
		{
			var context = value as TransformationContext;

			while ( context != null && !context.enabled ) context = context.parent;

			return context;
		}

		public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
		{
			throw new NotImplementedException();
		}

		#endregion
	}
}